package org.scratchcrew.web.cometd;

import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.Predicate;
import org.cometd.Client;
import org.cometd.Message;
import org.cometd.SecurityPolicy;

/**
 * Chains a list of {@link SecurityPolicy} instances in an aggregated policy. If
 * there is at least one policy in the chain that forbids something then it is
 * forbidden.
 */
public class SecurityPolicyChain implements SecurityPolicy {
	private final List<SecurityPolicy> securityPolicyChain;

	public SecurityPolicyChain(List<SecurityPolicy> securityPolicyChain) {
		this.securityPolicyChain = securityPolicyChain;
	}

	private static abstract class PolicyPredicate implements Predicate {
		@Override
		public boolean evaluate(Object object) {
			SecurityPolicy policy = (SecurityPolicy) object;
			boolean can = can(policy);
			// If !can then we found a policy that forbids the predicate.
			return !can;
		}

		public abstract boolean can(SecurityPolicy policy);
	}

	private boolean check(final PolicyPredicate predicate) {
		Object result = CollectionUtils.find(securityPolicyChain, predicate);
		return result == null;
	}

	@Override
	public boolean canCreate(final Client client, final String channel,
			final Message message) {
		return check(new PolicyPredicate() {
			@Override
			public boolean can(SecurityPolicy policy) {
				return policy.canCreate(client, channel, message);
			}
		});
	}

	@Override
	public boolean canHandshake(final Message message) {
		return check(new PolicyPredicate() {
			@Override
			public boolean can(SecurityPolicy policy) {
				return policy.canHandshake(message);
			}
		});
	}

	@Override
	public boolean canPublish(final Client client, final String channel,
			final Message message) {
		return check(new PolicyPredicate() {
			@Override
			public boolean can(SecurityPolicy policy) {
				return policy.canPublish(client, channel, message);
			}
		});
	}

	@Override
	public boolean canSubscribe(final Client client, final String channel,
			final Message message) {
		return check(new PolicyPredicate() {
			@Override
			public boolean can(SecurityPolicy policy) {
				return policy.canSubscribe(client, channel, message);
			}
		});
	}

}
